/**
 * PAcote.
 */
package br.org.bertol.mestrado.engine;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.optimisation.entity.AbstractUnit;
import br.org.bertol.mestrado.engine.optimisation.entity.StoreResult;

/**
 * @author contaqualquer
 * @param <T>
 *            Tipo de objeto do problema
 */
public abstract class AbstractSearchEngine<T> extends Thread {
    // 1 starter para todos as her�sticas
    /***/
    protected transient CountDownLatch                   startHeuristic   = new CountDownLatch(
                                                                                  1);

    // 5 contadores de término de heuristica
    // para cada resultado conseguido no greedy
    /***/
    protected transient CountDownLatch                   doneHeuristic;

    /***/
    protected transient CountDownLatch                   startSystem;

    /***/
    protected transient CountDownLatch                   stopSystem;

    /***/
    protected transient String                           systemID;

    /***/
    protected transient String                           storePath;

    /***/
    protected transient int                              numIteracoes;

    /***/
    protected transient Verify<T>                        verify;

    /***/
    final transient EnumSet<Objective>                   typeEvaluation;

    /***/
    protected transient RandomSolution<T>                randomSolution;

    /***/
    protected final transient List<List<StoreResult<T>>> consolidateResult;

    /** Marca início da execução do processso. */
    protected long                                       initTime;

    /** Marca fim da execução do processso. */
    protected long                                       stopTime;

    /***/
    private final transient int                          MINUTE_CONVERTER = 60000;

    /**
     * @param startS
     *            Barreira de início
     * @param stopS
     *            Barreira de fim
     * @param numEval
     *            Número de avaliação
     * @param storeP
     *            Caminho onde salvar resultados
     * @param sID
     *            Identificação do sistema
     * @param objectives
     *            Objetivos do problema
     */
    public AbstractSearchEngine(final CountDownLatch startS,
        final CountDownLatch stopS, final int numEval, final String storeP,
        final String sID,
        final Objective... objectives) {
        super();

        // inicia inst�ncia do verificador
        verify = new Verify<T>(objectives, storeP, sID);

        typeEvaluation = EnumSet.noneOf(Objective.class);

        typeEvaluation.addAll(Arrays.asList(verify.getObjectives()));

        setName("Sistema " + sID + " | Tipo avaliação " + typeEvaluation
                + " | Nº de iterações " + numEval);

        startSystem = startS;

        stopSystem = stopS;

        systemID = sID;

        storePath = storeP;

        numIteracoes = numEval;

        randomSolution = new RandomSolution<T>();

        randomSolution.setVerify(verify);

        consolidateResult = new ArrayList<List<StoreResult<T>>>();

        initTime = 0;

        stopTime = 0;
    }

    /**
     * Armazena e consolida os resultados.
     * @param units
     *            Lista de resultados brutos
     */
    protected final void storeResult(final List<? extends AbstractUnit<T>>
            units) {

        final List<StoreResult<T>> results = new ArrayList<StoreResult<T>>();

        for (final AbstractUnit<T> unit : units) {

            results.add(unit.getConsolidateResult());

        }

        consolidateResult.add(results);

    }

    /**
     * Salva o resultado em planilha excel.
     * @param workbook
     *            Arquivo excel
     */
    public final void getResultSheet(final Workbook workbook) {

        int rowCounter = 0;

        final Short borderColor = IndexedColors.GREY_50_PERCENT.getIndex();

        // cria fonte cabeçaçho
        final Font fontHeader = workbook.createFont();
        fontHeader.setFontHeightInPoints(Short.valueOf("10"));
        fontHeader.setBoldweight(Font.BOLDWEIGHT_NORMAL);
        fontHeader.setColor(IndexedColors.WHITE.getIndex());

        // estilo do cabeçalho
        final CellStyle styleHeader = workbook.createCellStyle();
        styleHeader.setFont(fontHeader);
        styleHeader.setFillForegroundColor(IndexedColors.GREEN
                .getIndex());
        styleHeader.setFillPattern(CellStyle.SOLID_FOREGROUND);
        styleHeader.setBorderLeft(CellStyle.BORDER_THIN);
        styleHeader.setBorderBottom(CellStyle.BORDER_THIN);
        styleHeader.setBottomBorderColor(borderColor);

        // cria fonte corpo
        final Font fontBody = workbook.createFont();
        fontBody.setFontHeightInPoints(Short.valueOf("10"));
        fontBody.setBoldweight(Font.BOLDWEIGHT_NORMAL);

        // estilo do corpo
        final CellStyle styleBody = workbook.createCellStyle();
        styleBody.setFont(fontBody);
        styleBody.setBorderLeft(CellStyle.BORDER_THIN);
        styleBody.setFillForegroundColor(IndexedColors.WHITE
                .getIndex());
        styleBody.setFillPattern(CellStyle.SOLID_FOREGROUND);
        styleBody.setBorderBottom(CellStyle.BORDER_THIN);
        styleBody.setBottomBorderColor(borderColor);

        // cria fonte rodapé
        final Font fontFooter = workbook.createFont();
        fontFooter.setFontHeightInPoints(Short.valueOf("10"));
        fontFooter.setBoldweight(Font.BOLDWEIGHT_NORMAL);
        fontFooter.setColor(IndexedColors.WHITE.getIndex());

        // estilo do cabeçalho
        final CellStyle styleFooter = workbook.createCellStyle();
        styleFooter.setFont(fontFooter);
        styleFooter.setFillForegroundColor(IndexedColors.GREY_50_PERCENT
                .getIndex());
        styleFooter.setFillPattern(CellStyle.SOLID_FOREGROUND);
        styleFooter.setBorderLeft(CellStyle.BORDER_THIN);
        styleFooter.setBorderBottom(CellStyle.BORDER_THIN);
        styleFooter.setBottomBorderColor(borderColor);

        Sheet sheet = workbook.getSheet(systemID);

        if (sheet == null) {
            sheet = workbook.createSheet(systemID);

        } else {

            rowCounter = sheet.getLastRowNum() + 2;
        }

        for (final List<StoreResult<T>> storeResults : consolidateResult) {

            final Row header = sheet.createRow(rowCounter);

            int cellCouter = 0;

            header.createCell(cellCouter)
                    .setCellValue("Nº Mutantes");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Cobertura");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Operadores");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Casos de teste");
            header.getCell(cellCouter).setCellStyle(styleHeader);

            // adiciona dados da execução
            for (final StoreResult<?> storeResult : storeResults) {

                cellCouter = 0;

                final Row row = sheet.createRow(++rowCounter);

                for (final Objective objective : verify.getObjectives()) {

                    final int cellNum = row.getLastCellNum() < 0 ? 0 : row
                            .getLastCellNum();

                    final Cell cellVal = row.createCell(cellNum);

                    cellVal
                            .setCellValue(storeResult.getHeight()
                            .get(objective));
                    cellVal.setCellStyle(styleBody);
                    cellCouter++;

                }
                row.createCell(cellCouter)
                        .setCellValue(
                                      storeResult.getRoute());
                row.getCell(cellCouter).setCellStyle(styleBody);
                cellCouter++;

                row
                        .createCell(cellCouter)
                        .setCellValue(
                                      storeResult
                        .getAbstractCombinatorialFitness());

                row.getCell(cellCouter).setCellStyle(styleBody);
            }
        }

        // adiciona tempo de execução
        final Row footer = sheet.createRow(++rowCounter);

        footer.createCell(0)
                .setCellValue("Tempo de execução");

        footer.getCell(0).setCellStyle(styleFooter);

        footer.createCell(1)
                .setCellValue((stopTime - initTime) / MINUTE_CONVERTER);

        footer.getCell(1).setCellStyle(styleFooter);

    }

    /**
     * Retorna o stream de entrada de um arquivo.
     * Caso o arquivo não exista ele é criado.
     * @param fileName
     *            Arquivo para as entrada
     * @return {@link Workbook} Instância da entrada
     * @throws IOException
     *             Erro ao abrir arquivo
     */
    protected final Workbook getInputStream(final String fileName)
            throws IOException {

        final File file = new File(fileName);

        if (!file.exists()) {

            return new HSSFWorkbook();

        }

        return new HSSFWorkbook(new POIFSFileSystem(new FileInputStream(file)),
                true);

    }

}
